package org.example.video.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.commons.entity.Video;
import jakarta.annotation.Resource;
import org.example.video.service.VideoHistoryService;
import org.example.video.service.VideoRecommendService;
import org.example.video.service.VideoService;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @program: authority-v3.0.7
 * @ClassName:VideoServiceImpl
 * @description: 视频个性化推荐 service实现类
 * @author:dyy
 * @Version 3.0
 **/

@Service
public class VideoRecommendServiceImpl implements VideoRecommendService {
    @Resource
    private VideoService videoService;

    @Resource
    private VideoHistoryService historyService;


    /**
     * 推荐视频（使用当前时间戳作为随机种子）
     * @param userId 目标用户ID
     * @return 推荐视频列表（3个）
     */
    @Override
    public List<Video> recommendVideos(Long userId) {
        // 使用系统时间作为随机种子，保证不同时间调用会有不同随机效果
        return recommendVideos(userId, System.currentTimeMillis());
    }


    /**
     * 推荐视频核心实现方法
     * @param userId 目标用户ID
     * @param randomSeed 随机种子（用于保证可重复的随机结果）
     * @return 推荐视频列表（3个）
     */
    @Override
    public List<Video> recommendVideos(Long userId, Long randomSeed) {
        // 1. 获取用户最近的5个观看记录
        List<Video> recentVideos = historyService.getRecentVideos(userId, 5);

        // 如果没有观看历史，返回默认推荐（带随机种子）
        if (recentVideos.isEmpty()) {
            return getDefaultRecommend(randomSeed); // 修复参数传递
        }

        // 2. 提取最近观看视频的标签集合列表
        List<Set<String>> recentTagsList = recentVideos.stream()
                .map(v -> splitTags(v.getTags()))
                .collect(Collectors.toList());

        // 3. 获取候选视频（最近通过的100个视频）
        List<Video> candidates = videoService.list(
                new QueryWrapper<Video>()
                        .eq("statu", "已通过")
                        .last("LIMIT 100"));

        // 4. 计算每个候选视频的相似度得分
        Map<Long, Double> scores = new HashMap<>();
        candidates.forEach(video -> {
            Set<String> candidateTags = splitTags(video.getTags());
            // 计算与历史记录的最大相似度
            double maxSimilarity = recentTagsList.stream()
                    .mapToDouble(historyTags -> jaccardSimilarity(historyTags, candidateTags))
                    .max()  // 取历史记录中相似度最高的
                    .orElse(0);  // 空列表时返回0
            scores.put(video.getId(), maxSimilarity);
        });

        // 5. 排序和筛选逻辑
        return candidates.stream()
                // 过滤掉已观看的视频
                .filter(v -> !recentVideos.contains(v))
                // 主要排序：相似度降序，当差异<0.1时加入随机扰动
                .sorted((a, b) -> {
                    double diff = scores.get(b.getId()) - scores.get(a.getId());
                    // 相似度接近时随机排序
                    if (Math.abs(diff) < 0.1) {
                        // 使用固定种子保证可重复性
                        return new Random(randomSeed).nextInt(3) - 1;
                    }
                    return diff > 0 ? 1 : -1;
                })
                .limit(6)  // 先取前6个相似度较高的候选
                // 二次随机排序
                .sorted((a, b) -> new Random(randomSeed).nextInt(2) - 1)
                .limit(3)
                .collect(Collectors.toList());
    }


    /**
     * 将标签字符串转换为标签集合
     * @param tags 逗号分隔的标签字符串（如："电影,搞笑,娱乐"）
     * @return 标签集合（自动去重和去空格）
     */
    private Set<String> splitTags(String tags) {
        if (tags == null || tags.isEmpty()) {
            return new HashSet<>();
        }
        return Arrays.stream(tags.split(","))
                .map(String::trim)
                .collect(Collectors.toSet());
    }


    /**
     * 计算Jaccard相似度系数
     * @param set1 标签集合1
     * @param set2 标签集合2
     * @return 相似度值（范围0.0~1.0）
     */
    private double jaccardSimilarity(Set<String> set1, Set<String> set2) {
        // 处理两个空集合的情况
        if (set1.isEmpty() && set2.isEmpty()) return 0.0;

        // 计算交集大小
        int intersection = (int) set1.stream().filter(set2::contains).count();

        // 计算并集大小
        int union = set1.size() + set2.size() - intersection;
        return union == 0 ? 0.0 : (double) intersection / union;
    }


    /**
     * 获取默认推荐（带随机种子）
     * 注意：直接拼接SQL存在注入风险，应确保randomSeed来自可信来源
     * @param randomSeed 随机种子（用于SQL RAND函数）
     */
    private List<Video> getDefaultRecommend(Long randomSeed) {
        return videoService.list(new QueryWrapper<Video>()
                .eq("statu", "已通过")
                .orderByAsc("RAND(" + randomSeed + ")")
                .last("LIMIT 3"));
    }


    /**
     * 获取默认推荐（使用当前时间戳作为种子）
     */
    private List<Video> getDefaultRecommend() {
        return getDefaultRecommend(System.currentTimeMillis());
    }
}